package com.ardo.controller;

import cn.hutool.core.codec.Base64;
import cn.hutool.http.HttpUtil;

//import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ardo.entity.ResponseResult;
import com.ardo.entity.mall.WxLoginLog;
import com.ardo.entity.mall.WxUser;
import com.ardo.execption.code.BaseCodeEnum;
import com.ardo.service.UserService;
import com.ardo.utils.AppUtils;
import com.ardo.utils.JwtUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidParameterSpecException;
import java.util.*;

/**
 * 微信小程序登录 - 鉴权
 * 参考：https://blog.csdn.net/abcwanglinyong/article/details/80267901
 */
@RestController
@RequestMapping("/login")
public class LoginController {

    @Autowired
    private UserService userService;

    @RequestMapping("/wxlogin")
    public String wxlogin(HttpServletRequest request) {
        AppUtils.printTitle("小程序 微信用户登录鉴权");
        String result = "";

        String code = request.getParameter("code");

        Map map = new HashMap();
        // 登录凭证不能为空
        if (code == null || code.length() == 0) {
            System.out.println("code 不能为空");
            map.put("status", 0);
            map.put("msg", "code 不能为空");
            result = JSONObject.toJSONString(ResponseResult.success(map));
            return result;
        }

        // 小程序唯一标识 (在微信小程序管理后台获取)
        String wxspAppid = "wx654ceb998c63a5ed";
        // 小程序的 app secret (在微信小程序管理后台获取)
        String wxspSecret = "6c2b435129d56772888bc7e86a9490a6";
        // 授权（必填）
        String grant_type = "authorization_code";

        //1、向微信服务器 使用登录凭证 code 获取 session_key 和 openid
        // 请求参数
        String params = "appid=" + wxspAppid + "&secret=" + wxspSecret + "&js_code=" + code + "&grant_type="
                + grant_type;
        System.out.println("请求参数 -> " +params);


        try {
            // 发送请求
            String sr= HttpUtil.get("https://api.weixin.qq.com/sns/jscode2session"+"?"+params);
            //String sr = HttpRequest.sendGet("https://api.weixin.qq.com/sns/jscode2session", params);
            System.out.println("鉴权结果 -> " + sr);
            // 解析相应内容（转换成json对象）
            cn.hutool.json.JSONObject json = new cn.hutool.json.JSONObject(sr);
            // 获取会话密钥（session_key）
            String session_key = json.get("session_key").toString();
            // 用户的唯一标识（openid）
            String openid = (String) json.get("openid");

            System.out.println("openid="+openid+",session_key="+session_key);

            WxUser user = userService.findByOpenid(openid);
            if(user != null){
                //登陆日志
                WxLoginLog log = new WxLoginLog();
                log.setOpenid(openid);
                log.setSessionkey(session_key);
                log.setUsername(user.getName());
                userService.insertWxLoginLog(log);

                String token = JwtUtil.generateToken(user.getName(), openid, new Date());
                map.put("token", token);
                map.put("uid", openid);

                result = JSONObject.toJSONString(ResponseResult.success(map));
            }else{
                //需要去注册才可以登录
                map.put("tip", "需要去注册才可以登录");
                result = JSONObject.toJSONString(
                        new ResponseResult(BaseCodeEnum.LOGIN_ERROR_NO_REGISTER.getCode(), BaseCodeEnum.LOGIN_ERROR_NO_REGISTER.getMessage(), map));
            }


            System.out.println("小程序登录返回信息：" + result);

        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("--------------------- 小程序 微信用户登录鉴权 结束 ---------------------");
        return result;
    }

    /**
     * 注册
     * @param request
     * @return
     */
    @RequestMapping("/registerComplex")
    public Map registerComplex(HttpServletRequest request) {
        AppUtils.printTitle("小程序 微信用户注册");
        String code = request.getParameter("code");
        String encryptedData = request.getParameter("encryptedData");
        String iv = request.getParameter("iv");
        String referrer = request.getParameter("referrer");
        System.out.println("请求参数 -> code: "+code+" encryptedData: "+encryptedData+" iv: "+iv+" referrer: "+referrer);

        Map map = new HashMap();

        // 登录凭证不能为空
        if (code == null || code.length() == 0) {
            System.out.println("code 不能为空");
            map.put("status", 0);
            map.put("msg", "code 不能为空");
            return map;
        }

        // 小程序唯一标识 (在微信小程序管理后台获取)
        String wxspAppid = "wx654ceb998c63a5ed";
        // 小程序的 app secret (在微信小程序管理后台获取)
        String wxspSecret = "6c2b435129d56772888bc7e86a9490a6";
        // 授权（必填）
        String grant_type = "authorization_code";

        //1、向微信服务器 使用登录凭证 code 获取 session_key 和 openid
        // 请求参数
        String params = "appid=" + wxspAppid + "&secret=" + wxspSecret + "&js_code=" + code + "&grant_type="
                + grant_type;
        System.out.println("请求参数 -> " +params);


        try {
            // 发送请求
            String sr= HttpUtil.get("https://api.weixin.qq.com/sns/jscode2session"+"?"+params);
            //String sr = HttpRequest.sendGet("https://api.weixin.qq.com/sns/jscode2session", params);
            System.out.println("鉴权结果 -> " + sr);
            // 解析相应内容（转换成json对象）
            cn.hutool.json.JSONObject json = new cn.hutool.json.JSONObject(sr);
            // 获取会话密钥（session_key）
            String session_key = json.get("session_key").toString();
            // 用户的唯一标识（openid）
            String openid = (String) json.get("openid");

            System.out.println("openid="+openid+",session_key="+session_key);


            //2、对encryptedData加密数据进行AES解密
            JSONObject userInfoJSON = getUserInfo( encryptedData, session_key, iv );
            System.out.println("根据解密算法获取的userInfo="+userInfoJSON);
            Map userInfo = new HashMap();
            userInfo.put("openId", userInfoJSON.get("openId"));
            userInfo.put("nickName", userInfoJSON.get("nickName"));
            userInfo.put("gender", userInfoJSON.get("gender"));
            userInfo.put("city", userInfoJSON.get("city"));
            userInfo.put("province", userInfoJSON.get("province"));
            userInfo.put("country", userInfoJSON.get("country"));
            userInfo.put("avatarUrl", userInfoJSON.get("avatarUrl"));
            // 解密unionId & openId;
            if (userInfoJSON.containsKey("unionId")) {
                userInfo.put("unionId", userInfoJSON.get("unionId"));
            }
            map.put("userInfo", userInfo);

            String result = JSONObject.toJSONString(map);
            System.out.println("小程序登录成功 -> 用户信息 -> " + result);


            /**
             * 先查询数据库，是否存在该openid用户，若存在无需插入数据库；不存在则新增到用户表
             */
            WxUser user = userService.findByOpenid(openid);
            if(user == null){
                System.out.println("用户不存在，新增入库");
                //入库
                String skey = UUID.randomUUID().toString();
                user = new WxUser();
                user.setSkey(skey);
                user.setOpenid(openid);
                user.setSessionkey( session_key );
                user.setAddress(userInfo.get("country")+""+userInfo.get("province")+userInfo.get("city"));
                user.setGender(userInfo.get("gender")+"");
                user.setName(userInfo.get("nickName")+"");
                user.setAvatarUrl(userInfo.get("avatarUrl")+"");
                user.setAddrCountry(userInfo.get("country")+"");
                user.setAddrProvince(userInfo.get("province")+"");
                user.setAddrCity(userInfo.get("city")+"");

                userService.insertWxUser(user);

            }else{
                //已存在
                System.out.println("用户openid已存在，不需要插入！");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }


        System.out.println("--------------------- 小程序 微信用户注册 结束 ---------------------");
        return map;
    }

    /**
     * 校验token
     * @param request
     * @return
     */
    @RequestMapping("/checktoken")
    public String checktoken(HttpServletRequest request) {
//        AppUtils.printTitle("校验token");
        String result = "";
        String token = request.getParameter("token");
        Map<String,Object> map = JwtUtil.validateToken(token);
        if(map.get("openid") != null){
            result = JSONObject.toJSONString(ResponseResult.success("token校验通过！"));
        }else{
            System.out.println("【提示】    token校验未通过！");
            result = JSONObject.toJSONString(
                    new ResponseResult(BaseCodeEnum.ERR_MSG_TOKEN_ERR.getCode(), BaseCodeEnum.ERR_MSG_TOKEN_ERR.getMessage(), null));
        }

        return result;
    }


    //解密用户敏感数据获取用户信息
    public static JSONObject getUserInfo(String encryptedData,String sessionKey,String iv){
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decode(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.decode(iv);
        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init( Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return JSON.parseObject(result);
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
//            log.error(e.getMessage(), e);
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
//            log.error(e.getMessage(), e);
        } catch (InvalidParameterSpecException e) {
            e.printStackTrace();
//            log.error(e.getMessage(), e);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
//            log.error(e.getMessage(), e);
        } catch (BadPaddingException e) {
            e.printStackTrace();
//            log.error(e.getMessage(), e);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
//            log.error(e.getMessage(), e);
        } catch (InvalidKeyException e) {
            e.printStackTrace();
//            log.error(e.getMessage(), e);
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
//            log.error(e.getMessage(), e);
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
//            log.error(e.getMessage(), e);
        }
        return null;
    }

}
